#include <cassert>
#include <iostream>
using namespace std;

#include "VectorField2Render.h"
#include "FlowSimUtil.h"

using namespace FlowSim;
using namespace Ogre;
VectorField2Render::VectorField2Render(SceneManager* scnMgr):_scnMgr(scnMgr),_lines(0),_lineNode(0),
							     _dij(5)
{

}
/**
 *This method refreshes the current renderer with a new vf. It is assumed that the dimension of the
 *new VF equals to that of the old. We want to do this so that we could use the old storage space
 *instead of clearing the old storage and updating it with the new vector field.
 **/
void VectorField2Render::refresh(VectorField2* vf)
{
  //assert(vf->getSize() == _vf->getSize() && "trying to refresh when dimension of old vf and new vf differ");
  _vf = vf;
  update();
}

void VectorField2Render::setInput(VectorField2* vf)
{
  if(_lines != 0)
    {
      //delete _lines;
      _lines->clear();
    }
  else
    _lines = new DynamicLines(RenderOperation::OT_LINE_LIST);   
  _vf = vf;
  if(_lineNode != 0)
    {
      _scnMgr->getRootSceneNode()->removeAndDestroyChild("VectorField2d");
      _lineNode = 0;
    }
  _lineNode = _scnMgr->getRootSceneNode()->createChildSceneNode("VectorField2d");
  int size = _vf->getSize();
  _lineNode->setPosition(0,0,1.0);
  setupRender();
  //testBiLerp();
}

void VectorField2Render::update()
{
#ifdef DEBUG3
  cout << "VectorField2Render::update()" << endl;
#endif
  int size = _vf->getSize();
  Vector2* data = _vf->getData();
  int dij = _dij;
  Real offset = 0.5;
  _lines->clear();
  Vector3 temp;
  temp.z = 0.0;
  Vector3 head;
  for(int i = 0; i < size; i+=dij)
    {
      for(int j = 0; j < size; j+=dij)
	{
	  temp.x = 0.1*data[A_INDEX(i,j,size)].x;
	  temp.y = 0.1*data[A_INDEX(i,j,size)].y;
	  //temp = 0.1*data[A_INDEX(i,j,size)];
	  //temp.normalise();
	  _lines->addPoint((Real)i+offset,(Real)j+offset,0.0);
	  //_lines->addPoint(i+offset+data[A_INDEX(i,j,size)].x,
	  //	   j+offset+data[A_INDEX(i,j,size)].y,
	  //	   0.0);
	  _lines->addPoint(i+offset+temp.x,
			   j+offset+temp.y,
			   0.0);
	  //draw head
	  head = temp.crossProduct(Vector3::UNIT_Z);
	  _lines->addPoint(i+offset+temp.x+0.2*head.x,
			   j+offset+temp.y+0.2*head.y,
			   0.0);
	  _lines->addPoint(i+offset+temp.x-0.2*head.x,
			   j+offset+temp.y-0.2*head.y,
			   0.0);
	}
    }
  /*
  for(int i = 0; i < size*size; i++)
    {
      _lines->setPoint(i,data[i]);
    }
  */
  _lines->update();
  //_lineNode->needUpdate();
}
/**
 *This method setup the render for rendering of vector field. This method will use the class
 *DynamicLines to setup the vertex buffer which will render the current vector field as
 *lines.
 **/
void VectorField2Render::setupRender()
{
#ifdef DEBUG3
  cout << "---------VectorField2Render::setupRender()--------------" << endl;
#endif
  Real offset = 0.5;
  int dij = _dij;
  Vector2* data = _vf->getData();
  int size = _vf->getSize();
  int x = 1;
  int y = 1;
  Vector2 d,d2,d3,d4,d5,d6,d7,d8,tx,ty;
 
#ifdef DEBUG3
  cout << "1,0: " << endl;
  cout << "d5: " << d5.x << " " << d5.y << endl;
  cout << "d6: " << d6.x << " " << d6.y << endl;
  cout << "2,0: " << endl;
  cout << "tx: " << tx.x << " " << tx.y << endl;
  cout << "ty: " << ty.y << " " << ty.y << endl;
#endif

 //   for(int i=0;i<size-2;i++)
//      {
//        d = data[A_INDEX((size-1),(y+i),size)]; //right border
//        d2 = data[A_INDEX((size-2),(y+i),size)]; //left of right border
//        d3 = data[A_INDEX(0,(y+i),size)]; //left border
//        d4 = data[A_INDEX(1,(y+i),size)]; //right of left border
//        d5 = data[A_INDEX((x+i),0,size)]; //bottom border
//        d6 = data[A_INDEX((x+i),1,size)];
//        d7 = data[A_INDEX((x+i),(size-1),size)];
//        d8 = data[A_INDEX((x+i),(size-2),size)];
       
//        //right border
//        _lines->addPoint((Real)(size-1)+offset,(Real)(y+i)+offset,0.0); 
//        _lines->addPoint((Real)(size-1)+offset+d.x,(Real)(y+i)+offset+d.y,0.0);
//        _lines->addPoint((Real)(size-2)+offset,(Real)(y+i)+offset,0.0);
//        _lines->addPoint((Real)(size-2)+offset+d2.x,(Real)(y+i)+offset+d2.y,0.0);
//        //left border
//        _lines->addPoint((Real)(0)+offset,(Real)(y+i)+offset,0.0); 
//        _lines->addPoint((Real)(0)+offset+d3.x,(Real)(y+i)+offset+d3.y,0.0);
//        _lines->addPoint((Real)(1)+offset,(Real)(y+i)+offset,0.0);
//        _lines->addPoint((Real)(1)+offset+d4.x,(Real)(y+i)+offset+d4.y,0.0);
       
       
//        //bottom border
//        _lines->addPoint((Real)(x+i)+offset,0.0+offset,0.0); 
//        _lines->addPoint((Real)(x+i)+offset+d5.x,0.0+offset+d5.y,0.0);
//        _lines->addPoint((Real)(x+i)+offset,1.0+offset,0.0);
//        _lines->addPoint((Real)(x+i)+offset+d6.x,1.0+offset+d6.y,0.0);
      
//        //top border
//        _lines->addPoint((Real)(x+i)+offset,(Real)(size-1)+offset,0.0); 
//        _lines->addPoint((Real)(x+i)+offset+d7.x,(Real)(size-1)+offset+d7.y,0.0);
//        _lines->addPoint((Real)(x+i)+offset,(Real)(size-2)+offset,0.0);
//        _lines->addPoint((Real)(x+i)+offset+d8.x,(Real)(size-2)+offset+d8.y,0.0);
       
//      }

   //draw right border  
  for(int i = 0; i < size; i+=dij)
    {
      for(int j = 0; j < size; j+=dij)
	{
	  //_lines->addPoint(i,j,0.0);
	  _lines->addPoint((Real)i+offset,(Real)j+offset,0.0);
	  //_lines->addPoint(i+offset+0.5,j+offset+0.5,0.0);
	  _lines->addPoint(i+offset+data[A_INDEX(i,j,size)].x,j+offset+data[A_INDEX(i,j,size)].y,0.0);
	}
    }
  _lines->update();
  _lineNode->attachObject(_lines);
}

void VectorField2Render::testBiLerp()
{
  _lines->update();
  _lineNode->attachObject(_lines);
}

/**
 *
 * C12 ------------------- C22
 * |                       |
 * |         (x,y)         |
 * |                       |
 * C11 ------------------- C21
 **/
void VectorField2Render::addTestVec(const Vector2 pos, Vector2 vec)
{
#ifdef DEBUG3
  cout << "VectorField2Render::addTestVec()" << endl;
#endif
  Vector2 temp;
  Vector2* data = _vf->getData();
  Vector2 p = pos;;
  int size = _vf->getSize();
  int boffset = 3;
  //test for boundary conditions
  if(p.x >= size-boffset + 0.5) //outside the cell on the right
    {
      p.x = size-boffset + 0.5;
      temp.x = (size-(boffset+1))+0.5;
    }
  else if(p.x < 1.5) //outside the cell on the left
    {
      p.x = 1.5;
      temp.x = 1.5;
    }
  else
    temp.x = Math::Floor(p.x-0.5)+0.5;
  if(p.y >= size-boffset + 0.5) //outside the cell on the right
    {
      p.y = size-boffset+0.5;
      temp.y = (size-(boffset+1))+0.5;
    }
  else if(p.y < 1.5)
    {
      p.y = 1.5;
      temp.y = 1.5;
    }
  else
    temp.y = Math::Floor(p.y-.05)+0.5;
#ifdef DEBUG3
  cout << "Calculated boundary conditons:" << endl;
  cout << "temp: " << temp.x << " " << temp.y << endl;
  cout << "About to add the 4 neighbors to the screen." << endl;
#endif
  Real x,y;
  //render the 4 neighbors
  for(int i = 0; i < 2; i++)
    {
      for(int j = 0; j < 2; j++)
	{
	  x = temp.x + j;
	  y = temp.y + i;
	  //render vector from (x,y)
	  Vector2 d = data[A_INDEX((int)x,(int)y,size)];
#ifdef DEBUG3
	  cout << "i,j: " << i << " " << j << endl;
	  cout << "x,y: " << x << " " << y << endl;
	  cout << "(int)(x,y)" << (int)x << " " << (int)y << endl;	  
	  cout << "d: " << d.x << " " << d.y << endl;
#endif
	  _lines->addPoint(x,y,0.0);
	  _lines->addPoint(x+d.x,y+d.y,0.0);
	}
    }
#ifdef DEBUG3
  cout << "Finished adding the 4 neighbors to the screen." << endl;
  cout << "About to add the test vector" << endl;
  cout << "Pos: " << p.x << " " << p.y << endl;
  cout << "vec: " << vec.x << " " << vec.y << endl;
#endif
  //render the test vector
  _lines->addPoint(p.x,p.y,0.0);
  _lines->addPoint(p.x+vec.x,p.y+vec.y,0.0);
#ifdef DEBUG3
  cout << "About to do call _lines->update()" << endl;
#endif
  _lines->update();
#ifdef DEBUG3
  cout << "Finished _lines->update()" << endl;
#endif
}
